#include "bclient.h"
#include "bcurl.h"

using namespace bhttp;

bclient* bclient::instance()
{
	static bclient t;
	return &t;
}

void bclient::timeout_connect(int value)
{
	timeout_connect_ = value;
}

int bclient::timeout_connect() const
{
	return timeout_connect_;
}

void bclient::timeout_read(int value)
{
	timeout_read_ = value;
}

int bclient::timeout_read() const
{
	return timeout_read_;
}

void bclient::ssl_verfication(const std::string& ca)
{
	std::lock_guard<std::mutex> lk(ssl_ca_file_mtx_);
	ssl_ca_fname_ = ca;
}

void bclient::ssl_verfication(std::string&& ca)
{
	std::lock_guard<std::mutex> lk(ssl_ca_file_mtx_);
	ssl_ca_fname_ = std::move(ca);
}

const std::string& bclient::ssl_verfication() const
{
	std::lock_guard<std::mutex> lk(ssl_ca_file_mtx_);
	return ssl_ca_fname_;
}

void bclient::send(const std::shared_ptr<brequest>& req, bcallback cb)
{
	auto rep = std::make_shared<bresponse>();
	thpool_->run(std::bind(&bclient::process, this, req, rep, cb));
}

void bclient::send_immediate(const std::shared_ptr<brequest>& req, bcallback cb)
{
	auto rep = std::make_shared<bresponse>();
	auto t = std::thread(std::bind(&bclient::process, this, req, rep, cb));
	t.detach();
}

bclient::bclient()
	: thpool_(std::make_shared<bthreadpool>())
{
	thpool_->start(k_thd_cnt);
	if (CURLE_OK != curl_global_init(CURL_GLOBAL_DEFAULT))
	{
		fprintf(stderr, "error caught in curl_global_init\n");
		abort();
	}
}

bclient::~bclient()
{
	thpool_->stop();
	curl_global_cleanup();
}

void bclient::process(const std::shared_ptr<brequest>& req, std::shared_ptr<bresponse>& rep, bcallback cb)
{
	switch (req->method())
	{
	case brequest::type::GET: get(req, rep); break;
	case brequest::type::POST: post(req, rep); break;
	case brequest::type::DEL: del(req, rep); break;
    case brequest::type::PUT: put(req, rep); break;
    case brequest::type::PATCH: patch(req, rep); break;
	default: break;
	}
	if (cb) cb(req, rep);
}

void bclient::get(const std::shared_ptr<brequest>& req, std::shared_ptr<bresponse>& rep)
{
	int code = -1;
	bcurl cl;
	bool ok = cl.init(this, req, rep)
		&& cl.option(CURLOPT_FOLLOWLOCATION, true)
		&& cl.perform(&code);
	rep->code(code);
	rep->successed(ok);
}

void bclient::post(const std::shared_ptr<brequest>& req, std::shared_ptr<bresponse>& rep)
{
	int code = -1;
	bcurl cl;
	bool ok = cl.init(this, req, rep);
	if (!req->forms().empty())
		ok = (ok && cl.mime_init(req));
	else
		ok = (ok && cl.option(CURLOPT_POST, 1L)
		&& cl.option(CURLOPT_POSTFIELDS, req->body())
		&& cl.option(CURLOPT_POSTFIELDSIZE, req->body_size()));
	ok = (ok && cl.perform(&code));
	rep->code(code);
	rep->successed(ok);
}

void bclient::del(const std::shared_ptr<brequest>& req, std::shared_ptr<bresponse>& rep)
{
	int code = -1;
	bcurl cl;
	bool ok = cl.init(this, req, rep)
		&& cl.option(CURLOPT_CUSTOMREQUEST, "DELETE")
		&& cl.option(CURLOPT_FOLLOWLOCATION, true)
        && cl.option(CURLOPT_POSTFIELDS, req->body())
        && cl.option(CURLOPT_POSTFIELDSIZE, req->body_size())
		&& cl.perform(&code);
	rep->code(code);
	rep->successed(ok);
}

void bhttp::bclient::put(const std::shared_ptr<brequest>& req, std::shared_ptr<bresponse>& rep)
{
    int code = -1;
    bcurl cl;
    bool ok = cl.init(this, req, rep)
        && cl.option(CURLOPT_CUSTOMREQUEST, "PUT")
        && cl.option(CURLOPT_POSTFIELDS, req->body())
        && cl.option(CURLOPT_POSTFIELDSIZE, req->body_size())
        && cl.perform(&code);
    rep->code(code);
    rep->successed(ok);
}

void bhttp::bclient::patch(const std::shared_ptr<brequest>& req, std::shared_ptr<bresponse>& rep)
{
    int code = -1;
    bcurl cl;
    bool ok = cl.init(this, req, rep)
        && cl.option(CURLOPT_CUSTOMREQUEST, "PATCH")
        && cl.option(CURLOPT_POSTFIELDS, req->body())
        && cl.option(CURLOPT_POSTFIELDSIZE, req->body_size())
        && cl.perform(&code);
    rep->code(code);
    rep->successed(ok);
}
